use crate::database;
use crate::handler;

use crate::CustomResult::*;
use axum::Json;
use reqwest;
use serde::{Deserialize, Serialize};
use serde_json;
use tokio::try_join; //多个网络请求

//jwt
use crate::handler::jwt::{generate_jwt, Claims};
use std::borrow::BorrowMut;
use std::time::{Duration, SystemTime, UNIX_EPOCH};

/************************** 密码登录 ***************************/
// api
// '/api/login'

//请求参数
#[derive(Serialize, Deserialize, Debug, Default)]
pub struct RequestLogin {
    pub name: String,
    pub password: String,
}

//返回参数
#[derive(Serialize, Deserialize, Debug, Default)]
#[serde(default)]
pub struct ResponseLogin {
    pub code: i32,
    pub data: database::user::User,
    pub msg: Option<String>,
}

pub async fn login(Json(param): Json<RequestLogin>) -> CustomResult<Json<ResponseLogin>> {
    println!("login");
    // 1. 根据 name 获取用户集合 (已处理 不存在的用户名)
    let users = database::user::find_by_name(&param.name).await?;

    // 2. 检查密码是否匹配
    let user = users.into_iter().find(|user| {
        user.password
            .as_ref()
            .map_or(false, |password| password == &param.password)
    });
    let user = match user {
        Some(user) => user,
        _ => return Err(CustomError::TipError("密码错误".to_string())),
    };

    // 3. 刷新tokn
    let token = fresh_token_by_id(user.id).await;
    let res = match token {
        Ok(_) => Some(Json(ResponseLogin {
            code: 200,
            data: user,
            msg: None,
        })),
        Err(_) => None, // 刷新token失败，返回None
    };

    res.ok_or(CustomError::TipError("创建token失败".to_string()))
}

/************************** 微信小程序登录 ***************************/
// api
// '/api/wxmn_login'

//请求参数
#[derive(Serialize, Deserialize, Debug, Default)]
pub struct RequestWxmnLogin {
    pub wx_code: String,       //微信登录凭证 (获取openId)
    pub wx_phone_code: String, //微信手机号凭证  (获取手机号)
}

//返回参数
#[derive(Serialize, Deserialize, Debug, Default)]
#[serde(default)]
pub struct ResponseWxmnLogin {
    pub code: i32,
    pub data: database::user::User,
    pub msg: Option<String>,
}
//通过微信小程序端获取到的微信授权码（uni.login拿到）,  从服务端获取用户的微信openId, 获取到openId视为登录成功
pub async fn wxmn_login(
    Json(param): Json<RequestWxmnLogin>,
) -> CustomResult<Json<ResponseWxmnLogin>> {
    //获取微信用户唯一凭证
    let open_id = request_wx_open_id(&param.wx_code).await?;

    //通过open_id 查询所有用户
    let users = database::user::find_by_open_id(&open_id).await?;
    
    if users.len() == 0 {
        //无用户,新创建的用户
        let user_id = database::user::insert("微信用户", "", 0, "", "").await?;
        //更新新用户的open_id
        let _ = database::user::upsert_open_id_by_id(user_id as i32, &open_id).await?;
        //查询新用户
        let user = database::user::find_by_id(user_id as i32).await?;
        //刷新token
        let _ = fresh_token_by_id(user.id).await;
        //返回
        Ok(Json(ResponseWxmnLogin {
            code: 200,
            data: user.clone(),
            msg: None,
        }))
    } else {
        //已有微信用户(已有open_id)
        let user = users.get(0).unwrap();
        //刷新token
        let _ = fresh_token_by_id(user.id).await;
        //返回
        Ok(Json(ResponseWxmnLogin {
            code: 200,
            data: user.clone(),
            msg: None,
        }))
    }
}

//通过小程序端登录接口返回的code 获取微信用户的openId
async fn request_wx_open_id(wx_code: &str) -> CustomResult<String> {
    let client = reqwest::Client::new();
    /* 获取openId
    https://api.weixin.qq.com/sns/jscode2session?appid=<AppId>&secret=<AppSecret>&js_code=<code>&grant_type=authorization_code
     */
    let url = format!("https://api.weixin.qq.com/sns/jscode2session?appid=wx3f13cfdeb466a021&secret=bfa08be8addfc267b37b7451e23ddc87&js_code={}&grant_type=authorization_code",wx_code);
    println!("url={}", &url);
    let response = client
        .get(url)
        .timeout(Duration::from_secs(10))
        .send()
        .await
        .map_err(|e| e.to_string())?;
    /* 微信服务器返回格式
        {
        "openid":"xxxxxx",
        "session_key":"xxxxx",
        "unionid":"xxxxx",
        "errcode":0,
        "errmsg":"xxxxx"
    } */
    if response.status().is_success() {
        let res = response
            .json::<serde_json::Value>()
            .await
            .map_err(|e| CustomError::TipError(e.to_string()))?;
        let open_id = res["openid"].as_str().unwrap_or("");
        Ok(open_id.to_string())
    } else {
        Err(CustomError::TipError("获取openid失败".to_string()))
    }
}

//需要在微信公众平台-安全中心-IP白名单添加白名单 才能生效 暂时不用
async fn request_wx_phone(access_token: &str, wx_phone_code: &str) -> CustomResult<String> {
    let client = reqwest::Client::new();
    let url = "https://api.weixin.qq.com/wxa/business/getuserphonenumber";
    let param = serde_json::json!({
        "access_token": access_token,
        "code": wx_phone_code
    });

    println!("access_token={}", &access_token);

    let response = client
        .post(url)
        .json(&param)
        .header("Content-Type", "application/json")
        .timeout(Duration::from_secs(10))
        .send()
        .await
        .map_err(|e| CustomError::TipError(e.to_string()))?;

    if response.status().is_success() {
        let res = response
            .json::<serde_json::Value>()
            .await
            .map_err(|e| CustomError::TipError(e.to_string()))?;

        // 检查微信返回的错误码
        if res["errcode"].as_i64() == Some(0) {
            let phone_number = res["phone_info"]["phoneNumber"]
                .as_str()
                .ok_or_else(|| CustomError::TipError("缺少 phoneNumber".to_string()))?;
            Ok(phone_number.to_string())
        } else {
            Err(CustomError::TipError(format!(
                "微信返回错误: errcode={}, errmsg={}",
                res["errcode"], res["errmsg"]
            )))
        }
    } else {
        Err(CustomError::TipError("请求phoneNumber失败".to_string()))
    }
}

/************************** 注册 ***************************/
// api
// '/api/regist'

//请求参数
#[derive(Serialize, Deserialize, Debug, Default)]
pub struct RequestRegist {
    pub name: String,
    pub password: String,
}

//返回参数
// handler::user::ResponseCreate

//handler
pub async fn regist(
    Json(param): Json<RequestRegist>,
) -> CustomResult<Json<handler::user::ResponseCreate>> {
    let res1 = database::user::find_by_name(&param.name)
        .await?
        .into_iter()
        .filter(|user| {
            // 克隆 Option<String> 中的值，如果它存在的话
            user.name.as_ref().map_or("".to_string(), |s| (*s).clone()) == param.name
        })
        .collect::<Vec<database::user::User>>();

    if res1.len() == 1 {
        return Err("用户名已存在".into());
    }

    let request_create = handler::user::RequestCreate {
        name: param.name,
        password: param.password,
        gender: 0,
        email: "".to_string(),
        remark: "".to_string(),
    };

    handler::user::create(Json(request_create)).await
}

//插入或更新用户token
pub async fn fresh_token_by_id(id: i32) -> CustomResult<()> {
    //1 根据id 查询用户
    let user = database::user::find_by_id(id).await?;

    //2 根据id 产生token
    let token = create_token_by_id(user.id)?;

    //3 使用 id 和 token 更新db
    let res = database::user::upsert_token_by_id(id, &token).await;

    res
}

//传入user_id 获得一个新的token
pub fn create_token_by_id(id: i32) -> CustomResult<String> {
    //刷新token过期时间
    let now: SystemTime = SystemTime::now();
    let one_hour_later = now + Duration::from_secs(3600 * 24 * 30); // 3600 * 24 * 30    30天后过期
    let exp = one_hour_later
        .duration_since(UNIX_EPOCH)
        .expect("获取过期时间失败")
        .as_secs();

    let claims = Claims {
        user_id: id,
        exp: exp, //新的过期时间
    };
    let token = generate_jwt(&claims);

    token
}